//--------------------------------------------------------------------------------
// This file is part of the Windows Workflow Foundation Sample Code
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is intended only as a supplement to Microsoft
// Development Tools and/or on-line documentation.  See these other
// materials for detailed information regarding Microsoft code samples.
// 
// THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//--------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.ComponentModel.Compiler;
using System.Xml;
using System.Workflow.ComponentModel;
using System.Collections.ObjectModel;
using System.IO;
using System.Security.Principal;
using System.ComponentModel.Design;

namespace Microsoft.Samples.Workflow.UI
{
    public class NavigationManager : IDisposable
    {
        WorkflowRuntime runtime = null;

        public NavigationManager()
            : this(string.Empty)
        {
        }

        public NavigationManager(string configSection)
        {
            if (string.IsNullOrEmpty(configSection))
            {
                this.runtime = new WorkflowRuntime();
            }
            else
            {
                this.runtime = new WorkflowRuntime(configSection);
            }

            this.runtime.AddService(new SynchronousSchedulerService());
        }

        public WorkflowRuntime WorkflowRuntime
        {
            get { return this.runtime; }
        }

        public Guid CreateFlow(XmlReader xaml, XmlReader rules)
        {
            return this.CreateFlow(xaml, rules, null);
        }

        public Guid CreateFlow(XmlReader xaml, XmlReader rules, Dictionary<string, object> parameters)
        {
            WorkflowInstance instance = this.runtime.CreateWorkflow(xaml, rules, parameters);
            return instance.InstanceId;
        }

        public Guid CreateFlow(Type workflowType)
        {
            return this.CreateFlow(workflowType, null);
        }

        public Guid CreateFlow(Type workflowType, Dictionary<string, object> parameters)
        {
            WorkflowInstance instance = this.runtime.CreateWorkflow(workflowType, parameters);
            return instance.InstanceId;
        }

        public InteractionContext Start(Guid durableHandle)
        {
            WorkflowInstance instance = this.GetWorkflow(durableHandle);
            instance.Start();
            return GetCurrentContext(durableHandle);
        }

        public InteractionContext GoForward(Guid durableHandle, UserInput input)
        {
            InteractionContext currentContext = GetCurrentContext(durableHandle);
            ForwardCommand cmd = new ForwardCommand(input);
            if (currentContext != null)
            {
                WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
                workflowInstance.EnqueueItem(currentContext.Bookmark.ActivityName, cmd, null, null);
            }

            return cmd.NextInteraction;
        }

        public InteractionContext GoForward(Guid durableHandle, Bookmark bookmark, UserInput input)
        {
            WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
            ForwardCommand cmd = new ForwardCommand(input);
            workflowInstance.EnqueueItem(bookmark.ActivityName, cmd, null, null);

            return cmd.NextInteraction;
        }

        public InteractionContext GoBack(Guid durableHandle)
        {
            InteractionContext nextContext = null;
            ReadOnlyCollection<Bookmark> history = GetHistory(durableHandle);
            if (history != null && history.Count > 1)
            {
                // The history collection contains the current executing activity.
                // So to go back means go to the second to last entry.
                nextContext = Navigate(durableHandle, history[history.Count - 2]);
            }

            return nextContext;
        }

        public InteractionContext Navigate(Guid durableHandle, Bookmark bookmark)
        {
            WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
            SynchronizeCommand cmd = new SynchronizeCommand(bookmark);
            workflowInstance.EnqueueItem(workflowInstance.GetWorkflowDefinition().QualifiedName, cmd, null, null);
            return cmd.NextInteraction;
        }

        public void Terminate(Guid durableHandle)
        {
            try
            {
                WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
                workflowInstance.Terminate(SR.GetString(SR.Error_WorkflowShutdown, workflowInstance.InstanceId.ToString()));
            }
            catch (WorkflowTerminatedException)
            {
                throw new WorkflowCanceledException(SR.GetString(SR.Error_WorkflowCanceledByUser));
            }
        }

        public InteractionContext GetCurrentContext(Guid durableHandle)
        {
            GetContextCommand cmd = new GetContextCommand();
            WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
            workflowInstance.EnqueueItem(workflowInstance.GetWorkflowDefinition().QualifiedName, cmd, null, null);

            return cmd.Context;
        }

        public InteractionContext GetContext(Guid durableHandle, Bookmark bookmark)
        {
            GetContextCommand cmd = new GetContextCommand(bookmark);
            WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
            workflowInstance.EnqueueItem(workflowInstance.GetWorkflowDefinition().QualifiedName, cmd, null, null);

            return cmd.Context;
        }

        public ReadOnlyCollection<Bookmark> GetHistory(Guid durableHandle)
        {
            GetHistoryCommand cmd = new GetHistoryCommand();
            WorkflowInstance workflowInstance = this.GetWorkflow(durableHandle);
            workflowInstance.EnqueueItem(workflowInstance.GetWorkflowDefinition().QualifiedName, cmd, null, null);

            return cmd.History;
        }

        private WorkflowInstance GetWorkflow(Guid instanceId)
        {
            WorkflowInstance instance = null;
            if (!this.runtime.IsStarted)
            {
                this.runtime.StartRuntime();
            }

            try
            {
                instance = this.runtime.GetWorkflow(instanceId);
            }
            catch (InvalidOperationException e)
            {
                throw new WorkflowNotFoundException(e.Message, e);
            }

            return instance;
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.runtime != null)
                {
                    this.runtime.Dispose();
                    this.runtime = null;
                }
            }
        }
    }
}